#--
# Copyright (c) 2007 Maximilian Ott
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#++


module WBuilder
  module Show
    ############# SHOW ###############
    
    def _show_page_body(object_name, object, options, &block)
      @content.div(:class => "wshow") do
        @content.table(:class => :wshow) do
          yield
        end
        text @footer if @footer
      end
    end
    
    
    def _show_fields_for(object_name, *args, &block)
      raise ArgumentError, "Missing block" unless block_given?
      options = args.last.is_a?(Hash) ? args.pop : {}
      if (object_name && !@object.readable?(object_name))
        # not visible
        return
      end
      object  = args.first || @object.send(object_name)
      return if object.nil?
  
      current_object = @object
      @object = object
      
      current_object_name = @object_name
      @object_name = @object_name.kind_of?(Array) ? @object_name : [@object_name]
      @object_name << object_name
      index = options.delete :index
      @object_name << index if index
puts "WS>> object: #{@object}"
      block.call(@object, index) if block
#      yield @object
      @object_name = current_object_name
      @object = current_object
    end
  
    #
    # Options:
    #   * min_count - Min. number of records to show, stuff with empty ones if necessary
    # 
    def _show_array_for(object_name, *args, &block)
#      puts ">> ARRAY_FOR"
#      puts "<< ARRAY_FOR"
      
      raise ArgumentError, "Missing block" unless block_given?
      options = args.last.is_a?(Hash) ? args.pop : {}
      
      if (args.empty? && !@object.readable?(object_name))
        # not visible
        return
      end
      rec_array  = args.empty? ? @object.send(object_name) : args.first
#      rec_array= args.first || @object.__send__(object_name)
      puts "SA=> size: #{rec_array.size}"
      return if rec_array.size == 0
      
      opt = options.merge(:wrapper => :ul, 
        :wrapper_class => "#{object_name} rec_array", :wrapper_id => __unique_id)
      i = 1
      this = self
      __show_wrapper(object_name, opt, false) do 
        rec_array.each do |r|
          puts "aa-> #{r} #{r.class} #{object_name}  b:#{builder.object_id}"
          #@markaby << this.fields_for(object_name, r, options.merge(:index => i), &block)
          _show_fields_for(object_name, r, options.merge(:index => i), &block)
          i += 1
        end
      end
      
      
    end

    #
    # Show each element of 'array' in wrapper.
    #
    # Options:
    #   * min_count - Min. number of records to show, stuff with empty ones if necessary
    # 
    def _show_h(array, options = {}, &block)
      raise ArgumentError, "Missing block" unless block_given?
      opt = options.merge(:wrapper => :ul, 
        :wrapper_class => "rec_array", :wrapper_id => __unique_id)
      i = 1
      this = self
      __show_wrapper(nil, opt, false) do 
        rec_array.each do |r|
          _show_fields_for(nil, r, options.merge(:index => i), &block)
          i += 1
        end
      end
    end
    
    [:check_box, :date_select, :file_field, :password_field, :text_field, :text_area].each do |meth|
      define_method "_show_#{meth}" do | *args |
        options = args.last.is_a?(Hash) ? args.pop : {}
        method = args.first
        __show_wrapper(method, options)
      end
    end
    
    def _show_container(options = {}, &block)
      __show_wrapper(nil, options, &block)
    end
    
    def _show_selection(method, choices, options = {})
      options = options.reverse_merge({:label => 'Name'})
  #    puts "SELECT==> #{method} = #{value}  (c: #{current})"
      options = __show_choice_text(method, choices, options)
      __show_wrapper(nil, options)
    end
  
    def __show_choice_text(method, choices, options)
      
      current = @object.nil? ? nil : @object.send(method)
      
      # the current value needs to be in string format. From now (july/2011) onwards
      # foreign keys such as the RG are goin to be in INT format so you have to convert them to string.
      # learned it the hard way.. 
      begin
        current = current.to_s
      rescue
        current = current
      end
  #    @o = current; @m = method; @c = choices; breakpoint
      
      # TODO: This is going to be SLOW
      value = nil
      puts "CHOICE=>> #{choices.inspect}"
      choices.each do |c|
        v, k = c.kind_of?(Array) ? c : [c.kind_of?(Symbol) ? c.to_s.capitalize : c, c]
        #puts "CHOICE=>> #{k} = #{current} (#{k.class} #{current.class}"
        if k.to_s == current
          value = v 
        end
      end
      puts "SELECT==> #{method} = #{value}  (c: #{current})"
      options = options.merge({:value => value})
      options
    end
    
    #
    # Options:
    #
    # show:hide  .. Do not display
    # show:hide_if_false .. If true do not display anything if value is nil.
    # show:format .. format instructions
    #   - :yes_no .. show yes/no
    #   - :Yes_No .. show Yes/No
    #   - :label  .. if true use label as value, and hide if false
    #   - ['true value', 'false value'] .. 2D array of value to use
    #
    def _show_check_box(method, options = {})
      sopt = options[:show] || {}
      return if sopt[:hide]    
  
      v = __show_value(method, options) || false
      if !v && sopt[:hide_if_false]
        return
      end
      #raise "Expected boolean but got '#{v}:#{v.class}'" unless v.kind_of?(Boolean)
  
      # format
      value = v.to_s
      format = sopt[:format] || :default
      if format.kind_of?(Array)
        value = v ? format[0] : format[1]
      else
        case format
        when :yes_no
          value = v ? 'yes' : 'no'  
        when :Yes_No
          value = v ? 'Yes' : 'No'  
        when :label
          if v
            value = options[:label]
            options = options.merge(:supress_label => true, :label => '')
          else
            return
          end
        when :default
          value = v ? 'true' : 'false'
        else
          raise "Unknown format '#{format}'"
        end
      end
      __show_wrapper(method, options.merge(:value => value))
    end
  
    
    def __show_value(method, options)
      if (method && !@object.readable?(method))
        return nil
      end
      value = options[:value]
      if value.nil?
        if (choices = options.delete(:value_choices))
          options = __show_choice_text(method, choices, options)
          value = options[:value]
          puts "CHOICES(#{method}): #{value}::#{value.class}"
        elsif method
          value = @object.nil? ? nil : @object.send(method)
        end
      end
      value
    end
    
    def _show_radio_selection(method, choices, options = {})
      options = __show_choice_text(method, choices, options)
      __show_wrapper(method, options)
    end
    
    def _show_footer(&block)
      if block_given?
        @footer = capture do
          yield self
        end
      else
        @footer
      end
    end
    
    # Options:
    #  upload_label - Label of upload button ['Upload']
    #
    def _show_upload_field(method, *args)
      options = args.last.is_a?(Hash) ? args.pop : {}
      fields = args.empty? ? [:submit, :file] : args.first
      
      __show_wrapper(method, options) do
        hopts = options[:html] || {}
        hopts[:multipart] = true
        uopts = options[:url] || @options[:url] || {}
        url = {
          :action => uopts[:action] || :update, 
          :id => uopts[:id] || @object.id
        }
#        @content << form_tag({:url => url}, hopts)
        @content << form_tag(url, hopts)
        fields.each do |f|
          case f
          when :submit
            @content << submit_tag(options[:upload_label] || "Upload")
          when :file
            on = @object_name
            on = on.kind_of?(Array) ? on.dup : [@object_name]
            on << method
            m = options[:file_method] || 'file'
            it = WFormInstanceTag.new(on, m, self, @content, nil, @object)
            @content << it.to_input_field_tag('file', hopts)
          else
            if f.kind_of?(Array)
              proc, m = f
              on = @object_name
              on = on.kind_of?(Array) ? on.dup : [@object_name]
              on << method
              it = WFormInstanceTag.new(on, m, self, @content, nil, @object)
              proc.call(it)
            else
              raise "Unknown field '#{f}' in upload_field"
            end
          end
        end
        @content << '</form>'
      end
    end
  
    def __show_undefined_value
      missing 'undefined', :class => :undefined
    end

    #
    # options:
    #  * :supress_list => 1
    #  * :item_pos => :center (default), :left, :right
    #  * :label => string
    #  * :label_pos => :top, :bottom, :right
    #  * suppress_label
    #
    # If output is false, the result of 'block' is NOT being
    # pushed to markaby. Useful for 'macro' features, such as name_field
    #
    def __show_wrapper(method, options = {}, output = true, &block)
      puts "SHOW_WRAPPER: #{method}::#{method && !@object.readable?(method)}"
      if (method && !@object.readable?(method))
        # not visible
        puts "NOT VISIBLE"
        return
      end
      
      outer = options.has_key?(:supress_list) ? :nothing : :li
      if options.has_key?(:label)
        lname = options[:label]
      elsif !options.has_key?(:supress_label)
        lname = method.to_s.capitalize
      end
      label_pos = options[:label_pos] || :top
      this = self
      object = @object
      @content.tr do
        @content.td lname, :class => :label
        @content.td(:class => :value) do
          if block.nil?
            value = __show_value(method, options)
#            value = options[:value]
#  #          puts "WRAPPER+> v:#{value} m:#{method}"         
#            if value.nil? && method != nil
#              current = object.nil? ? nil : object.send(method)
#  #            puts "WRAPPER+> #{method} = #{current} (#{object})"
#              value = current.to_s
#            end
            if value.nil? || (value.kind_of?(String) ? value.empty? : false)
              this.__show_undefined_value
            else 
              text value
            end
          else
            t = yield(self)
          end
        end
      end
    end

    ########### SHOW COMPOUND #############
    
    def _show_name_field(method, fields = [], options = {})
      title = @object.name_title
      first = @object.name_first
      pref = @object.name_prefered
      family = @object.name_family
      
      if family.nil? || family.empty?
        n = ''
      else
        n = [title, family].join(' ')
        n += ", #{first}"
        n += " (#{pref})" if pref && !pref.empty?
      end
      options = options.reverse_merge({:label => 'Name', :value => n})
      __show_wrapper(nil, options)
    end
  
    def _show_address_field(method, options = {})
      options = options.reverse_merge({:label => 'Address'})
      method = method.to_s
      object = @object
      __show_wrapper(method, options) do
        addr = []
        ['1', '2'].each do |n|
          a = object.send(method + '_' + n)
          addr << a unless a.nil? || a.empty?
        end
  
        a = []
        ['city', 'state', 'postal'].each do |n|
           s = object.send(method + '_' + n)
           a << s unless s.nil? || s.empty?
        end
        as = a.join(', ')
        addr << as unless as.empty?
      
        a = object.send(method + '_country')
        addr << a unless a.nil? || a.empty?
  
        if addr.empty?
          __show_undefined_value
        else
          @content.ul do
            addr.each do |a|
              li a
            end
          end
        end
      end
    end
  
    def _show_phone_field(options = {})
      options = options.reverse_merge({:label => 'Phone'})
      ph = []
      ['phone', 'mobile'].each do |n|
        phone = @object.send(n)
        unless phone.nil? || phone.empty?
          ph << "#{phone} (#{n.capitalize})"
        end
      end
      __show_wrapper(:phone, options) do
        if ph.empty?
          __show_undefined_value
        else
          @content.ul do
            ph.each do |p|
              @content.li p
            end
          end
        end
        
      end
    end
    
    def _show_gender_selector(method, options = {})
      options = options.reverse_merge({:label => 'Gender'})
      radio_selection(method, [:male, :female], options)
    end
    
    def _show_section(title, descr = nil, &block)
      @content.div(:style => 'display:none') do
        @content.tr do
          @content.td(:colspan => 2) do
            @content.div(:class => :section) do
              @content.h3 title
              @content.p(descr) if descr
            end
          end
        end
        yield if block
      end
    end
    
  end
end
